Write‑up: Sample Smuggling [Quals]
Category: Forensic
Files: traffics.pcapng
Goal: Recover the hidden flag from captured network traffic.
1. Initial Analysis (Traffic Inspection)
I began by analyzing traffics.pcapng. There were many suspicious UDP packets targeting port 9890. The payloads contained binary data with a custom header pattern.
A hex dump of the UDP payloads revealed repeating markers: “CART” as the header and “TRAC” as the footer.
From external references, CaRT is a file format by Cybercentre Canada for securely storing malware samples. Its structure is:
- Header: metadata and RC4 key
- Encryption: RC4
- Compression: Zlib
2. Anomaly Discovery (Fragmentation)
The CaRT payloads were not complete files. Each UDP packet contained only a fragment of a larger dataset.
After decrypting a few CaRT fragments, I found they contained short Python scripts, for example:
import sys
dojLafKU = 40
EmRZOiPD = 92
zaWlhmEL = dojLafKU ^ EmRZOiPD
sys.stdout.write(chr(zaWlhmEL))
Each script XORs two integers to produce one ASCII character. This suggests the flag was split character‑by‑character, with each packet carrying exactly one character.
3. Ordering Problem
Because UDP does not guarantee ordering, the extracted characters were out of sequence.
By examining the byte immediately before the CART header (offset -1 from the payload), I observed a sequence like 0x00, 0x01, 0x02, etc.
Hypothesis: The byte preceding the CART header is the index of the character in the final flag.
4. Solution (Native PCAP Solver Script)
To solve it fully, I wrote solve_pcap.py to parse the PCAPNG file directly, decrypt CaRT fragments, and reassemble the flag.
Script Workflow
- PCAPNG Parsing: read Enhanced Packet Blocks.
- Packet Filtering: keep UDP packets with destination port 9890.
- Payload Extraction: extract UDP payload.
- CaRT Parsing: locate headers, RC4 key, and encrypted data.
- Decryption & Decompression: RC4 decrypt + Zlib decompress.
- Character Extraction: parse integers and XOR them.
- Reassembly: order by index byte and join characters.
Solver Script
import struct
import zlib
import re
# --- 1. RC4 Decryption Function ---
def rc4_crypt(data, key):
x = 0
box = list(range(256))
for i in range(256):
x = (x + box[i] + key[i % len(key)]) % 256
box[i], box[x] = box[x], box[i]
x = y = 0
out = bytearray()
for char in data:
x = (x + 1) % 256
y = (y + box[x]) % 256
box[x], box[y] = box[y], box[x]
out.append(char ^ box[(box[x] + box[y]) % 256])
return out
# --- 2. Simple PCAPNG & UDP Parser ---
def parse_pcapng(file_path):
with open(file_path, "rb") as f:
data = f.read()
offset = 0
packets = []
while offset < len(data):
try:
block_type = struct.unpack("<I", data[offset:offset+4])[0]
block_len = struct.unpack("<I", data[offset+4:offset+8])[0]
except struct.error:
break
# Block type 6 = Enhanced Packet Block
if block_type == 6:
cap_len = struct.unpack("<I", data[offset+20:offset+24])[0]
packet_data = data[offset+28 : offset+28+cap_len]
packets.append(packet_data)
offset += block_len
return packets
def parse_udp_payload(packet_data):
if len(packet_data) < 42:
return None
eth_type = struct.unpack(">H", packet_data[12:14])[0]
ip_offset = 14
if eth_type == 0x8100: # VLAN
eth_type = struct.unpack(">H", packet_data[16:18])[0]
ip_offset = 18
if eth_type != 0x0800:
return None
ver_ihl = packet_data[ip_offset]
ihl = (ver_ihl & 0x0F) * 4
protocol = packet_data[ip_offset + 9]
if protocol != 17:
return None
udp_offset = ip_offset + ihl
dst_port = struct.unpack(">H", packet_data[udp_offset+2:udp_offset+4])[0]
udp_len = struct.unpack(">H", packet_data[udp_offset+4:udp_offset+6])[0]
payload_offset = udp_offset + 8
payload = packet_data[payload_offset : payload_offset + (udp_len - 8)]
return dst_port, payload
# --- 3. Main Solver Logic ---
def solve_from_pcap(pcap_path):
print(f"[*] Analyzing {pcap_path}...")
raw_packets = parse_pcapng(pcap_path)
extracted_payloads = []
for pkt in raw_packets:
res = parse_udp_payload(pkt)
if res:
dst_port, payload = res
if dst_port == 9890:
extracted_payloads.append(payload)
print(f"[*] Found {len(extracted_payloads)} UDP packets on port 9890.")
results = []
for payload in extracted_payloads:
cart_idx = payload.find(b"CART")
if cart_idx == -1:
continue
idx_byte = payload[cart_idx - 1] if cart_idx > 0 else 0
header = payload[cart_idx : cart_idx + 38]
if len(header) < 38:
continue
arc4_key = header[14:30]
opt_len = struct.unpack("<Q", header[30:38])[0]
end_search = payload.find(b"TRAC", cart_idx)
if end_search == -1:
continue
encrypted_chunk = payload[cart_idx + 38 + opt_len : end_search]
try:
decrypted = rc4_crypt(encrypted_chunk, arc4_key)
decompressed = zlib.decompress(decrypted)
text = decompressed.decode("utf-8", errors="ignore")
nums = re.findall(r"(\d+)", text)
if len(nums) >= 2:
char = chr(int(nums[0]) ^ int(nums[1]))
results.append((idx_byte, char))
except:
pass
results.sort(key=lambda x: x[0])
flag = "".join(x[1] for x in results)
print("\n[+] RECOVERED FLAG:")
print("-" * 50)
print(flag)
print("-" * 50)
if __name__ == "__main__":
solve_from_pcap("traffics.pcapng")
5. Final Result
Running:
python solve_pcap.py
Recovered the full flag:
{found_this_cool_method_to_store_malwares_https://github.com/CybercentreCanada/cart}